package org.xx.armory.config;

import org.xx.armory.commons.SysUtils;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import static org.xx.armory.commons.Validators.notBlank;
import static org.xx.armory.commons.Validators.notNull;

abstract class FileConfigurationProvider
        extends AbstractConfigurationProvider
        implements ConfigurationProvider {
    private final URL url;
    private final String fileName;

    /**
     * 构造使用文件的配置提供程序。
     *
     * @param url
     *         包含配置内容的文件的资源地址。
     * @throws IllegalArgumentException
     *         如果参数{@code url}是{@code null}。
     */
    protected FileConfigurationProvider(
            URL url
    ) {
        this.url = notNull(url, "url");
        this.fileName = "";
    }

    /**
     * 构造使用文件的配置提供程序。
     *
     * @param fileName
     *         包含配置内容的文件名，该文件名应当是相对于类路径的文件名。
     * @throws IllegalArgumentException
     *         如果参数{@code fileName}是{@code null}或者只包含空白内容。
     */
    protected FileConfigurationProvider(
            String fileName
    ) {
        this.url = null;
        this.fileName = notBlank(fileName, "fileName").trim();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected final Configuration load() {
        try (final InputStream stream = new BufferedInputStream(this.openInputStream())) {
            return load(stream);
        } catch (IOException ex) {
            throw new IllegalStateException("cannot load configuration", ex);
        }
    }

    /**
     * 加载配置项。
     *
     * @param stream
     *         包含配置内容的流。
     * @return 已加载的配置项。
     * @throws IOException
     *         如果读取文件内容出错。
     */
    protected abstract Configuration load(
            InputStream stream
    )
            throws IOException;

    /**
     * 打开输入流。
     *
     * <p>如果指定了URL则使用URL打开输入流，否则使用文件名。</p>
     *
     * @return 用于读取配置的输入流。
     * @throws IOException
     *         打开输入流时出现错误。
     * @throws IllegalStateException
     *         未指定URL，也未指定文件名。
     */
    private InputStream openInputStream()
            throws IOException {
        if (this.url != null) {
            return this.url.openStream();
        } else if (!this.fileName.isEmpty()) {
            return SysUtils.openInputStream(this.fileName);
        } else {
            throw new IllegalStateException("uri and file name are both invalid");
        }
    }
}
